#include "xmldocument.h"
#include "XMLParser.h"
#include "XmlAttribute.h"
#include "XMLFactory.h"
#include "tree.hh"
#include "xmlexceptions.h"
#include <fstream>
#include <sstream>



//runs depth-first traversal to test tree was built
void xml_document::debug_print() {
	tree<std::shared_ptr<xmlnode>>::iterator iter = m_tree.begin();

	for (; iter != m_tree.end(); ++iter){
		printf("Node name: %s\n", (*iter)->get_name().c_str());
		printf("Depth: %d\n", m_tree.depth(iter));
		printf("Innertext (if any): %s\n", (*iter)->get_innertext().c_str());

		if ((*iter)->get_attributes().size()){
			printf("Attributes: \n");

			for (int i = 0; i < (*iter)->get_attributes().size(); ++i){
				printf("Attr %d: %s , %s\n", 
					i,
					(*iter)->get_attributes()[i].get_name().c_str(),
					(*iter)->get_attributes()[i].get_value().c_str());
			}
		}
	}
}

void xml_document::load_from_string( const std::string & inStr ){
    xml_parser p;

    p.parse(inStr);

    std::queue<ParseEle> & eleQue = p.get_token_queue();

    //Build the tree.
    //The queue has been validated properly.
    //
    //Set the root.
    tree<std::shared_ptr<xmlnode>>::iterator par;
	std::shared_ptr<xmlnode> root = xml_factory::create_node();
    root->set_name(eleQue.front().txt);
	
	//XXX Don't duplicate this
	xmlattributes attrs;
	for (int i = 0; i < eleQue.front().attrs.size(); ++i){
		attrs.push_back(
			xmlattribute(eleQue.front().attrs[i].first, eleQue.front().attrs[i].second)
			);
	}

	root->set_attributes(attrs);
	//

    par = m_tree.insert(m_tree.begin(), root);

    root->set_node_reference(par.node);
    eleQue.pop();

    while (eleQue.size() != 0 ){

        //printf("Type: %d\n", eleQue.front().pt);
        //printf("Name: %s\n", eleQue.front().txt.c_str());

        switch(eleQue.front().pt) {
            case PARSE_TYPE::OPEN :
            {
                std::shared_ptr<xmlnode> newele = xml_factory::create_node();
                //Set what we need to with the ele

                newele->set_name(eleQue.front().txt );         
                //A new node, will be child of our current node.

				//XXX This can go in the same function as above
				xmlattributes attrs;
				attrs.resize(eleQue.front().attrs.size());
				for (int i = 0; i < eleQue.front().attrs.size(); ++i){
					attrs.push_back(
						std::move(xmlattribute(eleQue.front().attrs[i].first, eleQue.front().attrs[i].second))
						);
				}
				newele->set_attributes(attrs);
				//
                par = m_tree.append_child(par, newele);
                newele->set_node_reference(par.node);

				if (eleQue.front().needClose == false){
					par = m_tree.parent(par);
				}

                break;
            }
            case PARSE_TYPE::CLOSE :
            {
                par = m_tree.parent(par);
                break;
            }
            case PARSE_TYPE::TEXT :
            {
                //This will add text to an element
				(*par)->set_innertext(eleQue.front().txt);
                break;
            }

            default :
                break;

        }
        eleQue.pop();
    }

	//debug_print();

}

void xml_document::load_from_file(const std::string & file){
    std::ifstream ifs;
    
	ifs.open(file.c_str());
    
    if (!ifs.is_open()){
		throw xml_filenotfound_exception();
    }
    
	std::string filestr((std::istreambuf_iterator<char>(ifs)),
                        std::istreambuf_iterator<char>());
    
   load_from_string(filestr);
}

//Gets the root node from the tree.
std::shared_ptr<xmlnode> xml_document::get_root_node(){
    
    if (m_tree.size() == 0){
        return NULL;
    }
    
    return *(m_tree.begin());
}

xmlnodes xml_document::get_elements_by_tag_name(const std::string & tagstr){
    //First search the tree for all elements with this particular tag name
    
    xmlnodes retval;
    
    if (m_tree.size() == 0){
        return retval;//empty retval
    }
    
    for(tree<std::shared_ptr<xmlnode>>::iterator it = m_tree.begin();
        it != m_tree.end();
        ++it){
        if ( (*it)->get_name() == tagstr){
            retval.push_back((*it));
        }
    }
    
    return retval;
}

xmlnodes xml_document::get_elements_by_id(const std::string & id){

	xmlnodes retval;
	
 
    if (m_tree.size() == 0){
        return retval;//empty retval
    }
    
    for(tree<std::shared_ptr<xmlnode>>::iterator it = m_tree.begin();
        it != m_tree.end();
        ++it){
			xmlattributes attrs = (*it)->get_attributes();
			for (int i = 0; i < attrs.size(); ++i){
				if (attrs[i].get_name() == "id" &&
					attrs[i].get_value() == id){
						retval.push_back((*it));
				}
			}
    }

	return retval;
}